#include "lib.h"

#include <cstdio>
#include <exception>
#include <numeric>

// 求平方
my_float_t square(my_float_t x) {
    return x * x;
}

// 求绝对值
my_float_t absolute(my_float_t x) {
    if (x < 0) {
        return -x;
    }
    else {
        return x;
    }
}

// 求平均值
my_float_t average(my_float_t x, my_float_t y) {
    return (x + y) / 2.0;
}

// 求乘方（幂）
my_float_t power(my_float_t a, unsigned int n) {
    if (n == 0) {
        return 1;
    }
    else if (n == 1) {
        return a;
    }
    else {
        return a * power(a, n - 1);
    }
}

// 求倒数
my_float_t reciprocal(my_float_t n) {
    return 1.0 / n;
}

// 足够好，是指误差足够小
bool good_enough(my_float_t a, my_float_t epsilon, my_float_t guess) {
    auto error = absolute(square(guess) - a);
    return error < epsilon;
}

my_float_t improve(my_float_t a, my_float_t guess) {
    return average(guess, (a / guess));
}

// 牛顿法求平方根
my_float_t sqrt_iter(my_float_t a, my_float_t epsilon, my_float_t guess) {
    // 打印中间结果
    fprintf(stdout, "%.18Lf\n", guess);

    if (good_enough(a, epsilon, guess)) {
        return guess;
    }
    else {
        auto guess_new = improve(a, guess);
        if (absolute(guess_new - guess) <= std::numeric_limits<my_float_t>::epsilon()) {
            // 精度有限，无法继续了
            return guess;
        }
        return sqrt_iter(a, epsilon, guess_new);
    }
}

// 求 a 的平方根，从 1.0 开始猜测，误差希望精确到小数点后 accuracy 位（前提是计算机的精度支持）
my_float_t sqrt(my_float_t a, unsigned int accuracy) {
    try {
        auto epsilon = reciprocal(power(10, accuracy));
        return sqrt_iter(a, epsilon, 1.0);
    }
    catch (std::exception const& ex) {
        fprintf(stderr, "Failed to sqrt, %s\n", ex.what());
    }
    catch (...) {
        fprintf(stderr, "Uncaught exception\n");
    }
    return 0.0;
}
